package references

import (
	"hi-jvm/instructions/base"
	"hi-jvm/instructions/util"
	"hi-jvm/rtda"
	"hi-jvm/rtda/heap"
)

/*
 调用函数分成多种实现的原因：
1： invokestatic指令调用静态函数
2： invokespecial指令
	1） 调用私有方法： 私有方法不需要动态绑定，可以加快调用速度
	2） super调用超类方法，不能用invokevirtual指令，否则会陷入无限循环
3： invokeinterface指令：效率考虑
 */
// Invoke interface method
// 指令操作码后跟着4个字节：
// 前面两个字节是一个16位的运行时常量索引池，
// 第三个字节是给方法传递参数所需要的slot数， 含义与给Method定义argSlotCount相同
// 第四个字节留给Oracle的某些java虚拟机实现用，它的值必须是0
type INVOKE_INTERFACE struct {
	index uint
	// count uint8
	// zero uint8
}

func (self *INVOKE_INTERFACE) FetchOperands(reader *util.BytecodeReader) {
	self.index = uint(reader.ReadUInt16())   // 前面两个字节是一个16位的运行时常量索引池，
	reader.ReadUInt8() // count   第三个字节是给方法传递参数所需要的slot数， 含义与给Method定义argSlotCount相同
	reader.ReadUInt8() // must be 0  第四个字节留给Oracle的某些java虚拟机实现用，它的值必须是0
}

func (self *INVOKE_INTERFACE) Execute(frame *rtda.Frame) {
	methodRef, ref  :=self.getMethodRef(frame)
	methodToBeInvoked := self.getMethodTobeInvoked(methodRef, ref)
	base.InvokeMethod(frame, methodToBeInvoked)
}

func (self *INVOKE_INTERFACE)getMethodTobeInvoked(methodRef *heap.InterfaceMethodRef,  ref *heap.Object)  *heap.Method  {
	methodToBeInvoked := heap.LookupMethodInClass(ref.Class,
		methodRef.Name, methodRef.Descriptor)
	if methodToBeInvoked == nil || methodToBeInvoked.IsAbstract() {
		panic("java.lang.AbstractMethodError")
	}
	if !methodToBeInvoked.IsPublic() {
		panic("java.lang.IllegalAccessError")
	}
	return methodToBeInvoked
}


func (self *INVOKE_INTERFACE) getMethodRef(frame *rtda.Frame) (methodRef *heap.InterfaceMethodRef,  ref *heap.Object) {

	cp := frame.Method.Class.ConstantPool
	methodRef = cp.GetConstant(self.index).(*heap.InterfaceMethodRef)
	resolvedMethod := methodRef.ResolvedInterfaceMethod()
	if resolvedMethod.IsStatic() || resolvedMethod.IsPrivate() {
		panic("java.lang.IncompatibleClassChangeError")
	}

	ref = frame.OperandStack.GetRefFromTop(resolvedMethod.ArgSlotCount - 1)
	if ref == nil {
		panic("java.lang.NullPointerException") // todo
	}
	if !ref.Class.IsImplements(methodRef.ResolvedClass()) {
		panic("java.lang.IncompatibleClassChangeError")
	}
	return
}

